﻿/******************************************************************** 
 * Cerebrum Embedded System Design Automation Framework
 * Copyright (C) 2010  The Pennsylvania State University
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ********************************************************************/
/******************************************************************** 
 * MappingCanvasControl.xaml.cs
 * Name: Matthew Cotter
 * Date: 13 Sep 2010 
 * Description: Represents a general canvas on which the Mapping interface is displayed.
 * History: 
 * >> (22 Mar 2011) Matthew Cotter: Corrected bug that caused issues when reloading a previously mapped state, after removing a component from the system that existed in that previous state.
 * >> (25 Jan 2011) Matthew Cotter: Implemented initial support for auto-saving intermediate mapping state.
 * >> (22 Oct 2010) Matthew Cotter: Corrected bug in aspect ratio/resizing during drag and drop.
 *                                  Eliminated a bug causing an exception during drag and drop.
 * >> ( 7 Oct 2010) Matthew Cotter: Added basics of reading project files and creating/displaying state based on mapping algorithm.
 * >> (30 Sep 2010) Matthew Cotter: Added support for rendering text on background canvases.
 *                                  Added MappingComplete event to notify top level GUI that mapping has completed.
 * >> (28 Sep 2010) Matthew Cotter: Created basic implementation of 'item information' to display resource and mapping utilization.
 * >> (27 Sep 2010) Matthew Cotter: Reorganized query mode to generate an 'item information' event when an object is clicked.
 * >> (24 Sep 2010) Matthew Cotter: Added MessageEventController for propagating events to the top-level GUI.
 * >> (21 Sep 2010) Matthew Cotter: Added support for query mode, mode toggle and events generated by control to notify upper level that the mapping state has changed.
 * >> (17 Sep 2010) Matthew Cotter: Added support for drag/drop, child alignment, and customizable object colors.
 *                                  Added basic functions for creating UI controls from mapping components.
 *                                  Added simple interface for populating and changing state of algorithm library based on GUI, and vice versa.
 * >> (13 Sep 2010) Matthew Cotter: Created basic outline of a top-level container control within which all mapping controls are contained.
 * >> (13 Sep 2010) Matthew Cotter: Source file created -- Initial version.
 ********************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections;
using FalconMappingAlgorithm;
using FalconPathManager;
using System.Diagnostics;
using CerebrumSharedClasses;
using System.IO;

namespace CerebrumMappingControls
{
    /// <summary>
    /// Interaction logic for MappingCanvas.xaml
    /// </summary>
    [System.ComponentModel.DesignerCategory("Code")]
    public partial class MappingCanvasControl : UserControl, IMappingControl
    {
        private CerebrumProjectManager.ProjectManager _ProjMan;
        private const string DEFAULT_MAPPING_STATE_FILE = "mapping_state.xml";

        #region Object Background Colors
        private Brush COMPONENT_BRUSH = Brushes.Aqua;
        private Brush FPGA_BRUSH = Brushes.AntiqueWhite;

        private Brush GROUP_BRUSH = Brushes.PowderBlue;
        private Brush CLUSTER_BRUSH = Brushes.DodgerBlue;

        private Brush CONNECTION_BRUSH = Brushes.Green;
        private Brush LINK_BRUSH = Brushes.Red;

        private Brush MAPPED_AREA_BRUSH = Brushes.Teal;
        private Brush UNMAPPED_AREA_BRUSH = Brushes.Tan;
        #endregion

        /// <summary>
        /// Default constructor.  Initializes mapping canvas and registers event handlers
        /// </summary>
        public MappingCanvasControl()
        {
            InitializeComponent();
            DefaultAlignments();
            this.SizeChanged += new SizeChangedEventHandler(MappingCanvas_SizeChanged);

            this.UIMode = InterfaceMode.ManualMapping;

            // Grab/Release objects, Select objects
            this.MouseLeftButtonDown += new MouseButtonEventHandler(MappingCanvasControl_MouseLeftButtonDown);
            this.MouseLeftButtonUp += new MouseButtonEventHandler(MappingCanvasControl_MouseLeftButtonUp);
            // Drag objects
            this.MouseMove+=new MouseEventHandler(MappingCanvasControl_MouseMove);

            // Collapse/Restore objects
            this.MouseRightButtonUp += new MouseButtonEventHandler(MappingCanvasControl_MouseRightButtonUp);
            
            RecalibrateCanvasSize();
        }

        /// <summary>
        /// Indicates whether mapping has been completed successfully
        /// </summary>
        public bool MappingComplete
        {
            get
            {
                if (MapObjects == null)
                    return false;
                else
                    return (MapObjects.AllComponentsMapped());
            }
        }
        
        #region Collapsible Controls

        void MappingCanvasControl_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            IMappingControl imap = GetControlAtMouse(e.MouseDevice);
            if (imap != null)
            {
                ICollapsible cbox = Mapping.GetCollapsibleAncestor(imap);
                if (cbox != null)
                {
                    cbox.Toggle();
                    RedrawInterconnects();
                }
            }
        }

        #endregion 
        
        #region Mapped/Unmapped Regional Division
        /// <summary>
        /// Get or set the brush used to color the 'Mapped' portion of the default canvas
        /// </summary>
        public Brush MappedColor
        {
            get
            {
                return mapped.Background;
            }
            set
            {
                mapped.Background = value;
            }
        }
        /// <summary>
        /// Get the WPF canvas used for the 'Mapped' portion of the default canvas
        /// </summary>
        public Canvas Mapped
        {
            get
            {
                return mapped;
            }
        }
        private double dUnmappedSize;
        /// <summary>
        /// Get or set the percentage of vertical space used for housing unmapped components and groups
        /// </summary>
        public double UnmappedPct
        {
            get
            {
                return dUnmappedSize;
            }
            set
            {
                if (value < 0)
                    value = 0;
                if (value > 1)
                    value = 1;
                dUnmappedSize = value;
                RecalibrateCanvasSize();
            }
        }
        /// <summary>
        /// Get or set the brush used to color the 'Unmapped' portion of the default canvas
        /// </summary>
        public Brush UnmappedColor
        {
            get
            {
                return unmapped.Background;
            }
            set
            {
                unmapped.Background = value;
            }
        }
        /// <summary>
        /// Get the WPF canvas used for the 'Unmapped' portion of the default canvas
        /// </summary>
        public Canvas Unmapped
        {
            get
            {
                return unmapped;
            }
        }
        #endregion

        private void RecalibrateCanvasSize()
        {
            double mSize = this.Height * (1 - this.UnmappedPct);

            mapped.Margin = new Thickness(0, 0, 0, 0);
            mapped.SetValue(Canvas.LeftProperty, (double)0);
            mapped.SetValue(Canvas.TopProperty, (double)0);
            mapped.SetValue(Canvas.RightProperty, (double)this.Width);
            mapped.SetValue(Canvas.BottomProperty, (double)mSize);
            mapped.SetValue(Canvas.WidthProperty, this.Width);
            mapped.SetValue(Canvas.HeightProperty, mSize);

            unmapped.Margin = new Thickness(0, 0, 0, 0);
            unmapped.SetValue(Canvas.LeftProperty, (double)0);
            unmapped.SetValue(Canvas.TopProperty, (double)mSize);
            unmapped.SetValue(Canvas.RightProperty, (double)this.Width);
            unmapped.SetValue(Canvas.BottomProperty, (double)this.Height);
            unmapped.SetValue(Canvas.WidthProperty, this.Width);
            unmapped.SetValue(Canvas.HeightProperty, this.Height - mSize);

            drawingBoard.Margin = new Thickness(0, 0, 0, 0);
            drawingBoard.SetValue(Canvas.LeftProperty, (double)0);
            drawingBoard.SetValue(Canvas.TopProperty, (double)0);
            drawingBoard.SetValue(Canvas.RightProperty, this.Right);
            drawingBoard.SetValue(Canvas.BottomProperty, this.Bottom);
            drawingBoard.SetValue(Canvas.WidthProperty, this.Width);
            drawingBoard.SetValue(Canvas.HeightProperty, this.Height);

            Mapping.RenderTextToCanvas(mapped, MAPPED_AREA_BRUSH, Brushes.LightGray, "Mapped Components");
            Mapping.RenderTextToCanvas(unmapped, UNMAPPED_AREA_BRUSH, Brushes.DarkGray, "Unmapped Components");
        }
        private void MappingCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            RecalibrateCanvasSize();
        }

        #region Child Alignment/Display Properties
        /// <summary>
        /// Get or set the number of columns for auto-alignment on the canvas
        /// </summary>
        public int ColumnsPerCanvas { get; set; }
        /// <summary>
        /// Item spacing for alignment on the canvas
        /// </summary>
        public double SpacingPerCanvas { get; set; }
        /// <summary>
        /// Get or set the number of columns for auto-alignment within a cluster
        /// </summary>
        public int ColumnsPerCluster { get; set; }
        /// <summary>
        /// Item spacing for alignment within a cluster
        /// </summary>
        public double SpacingPerCluster { get; set; }
        /// <summary>
        /// Get or set the number of columns for auto-alignment within an FPGA
        /// </summary>
        public int ColumnsPerFPGA { get; set; }
        /// <summary>
        /// Item spacing for alignment within an FPGA
        /// </summary>
        public double SpacingPerFPGA { get; set; }
        /// <summary>
        /// Get or set the number of columns for auto-alignment within a group
        /// </summary>
        public int ColumnsPerGroup { get; set; }
        /// <summary>
        /// Item spacing for alignment within a group
        /// </summary>
        public double SpacingPerGroup { get; set; }

        /// <summary>
        /// Creates and sets default alignment properties for child alignment
        /// </summary>
        public void DefaultAlignments()
        {
            this.ColumnsPerCanvas = 4;
            this.ColumnsPerCluster = 1;
            this.ColumnsPerFPGA = 1;
            this.ColumnsPerGroup = 2;

            this.SpacingPerCanvas = 8.0;
            this.SpacingPerCluster = 8.0;
            this.SpacingPerFPGA = 5.0;
            this.SpacingPerGroup = 3.0;
        }

        /// <summary>
        /// Re-aligns all children and draws connections/links between them
        /// </summary>
        public void RefreshDisplay()
        {
            this.AlignChildren();
            if (!bDragging)
                this.RedrawInterconnects();
        }

        #endregion
        
        #region IMappingControl Implementation

        // Generic Properties
        /// <summary>
        /// Indicates whether the control can be aligned as a child
        /// </summary>
        public bool Alignable { get; set; }
        /// <summary>
        /// Indicates how many columns the control's children are aligned into
        /// </summary>
        public int AlignmentColumns { get; set; }
        /// <summary>
        /// Indicates minimum spacing between aligned children
        /// </summary>
        public double AlignmentSpacing { get; set; }
        /// <summary>
        /// Defines the mapping name of the control
        /// </summary>
        public string MappingName { get; set; }
        /// <summary>
        /// Defines the mapping ID of the control
        /// </summary>
        public string MappingID { get; set; }
        /// <summary>
        /// Top-level Mapping canvas control each control is associated with
        /// </summary>
        public MappingCanvasControl ParentCanvas { get; set; }
        /// <summary>
        /// Generic canvas exposed by each control
        /// </summary>
        public Canvas ObjectCanvas { get { return root; } }
        /// <summary>
        /// Specifies the brush used to paint the background canvas
        /// </summary>
        public Brush BackgroundBrush { get; set; }

        // Size and Location
        /// <summary>
        /// Defines the top coordinate of the control
        /// </summary>
        public double Top { get { return Mapping.GetTop(this); } set { Mapping.SetTop(this, value); } }
        /// <summary>
        /// Defines the left coordinate of the control
        /// </summary>
        public double Left { get { return Mapping.GetLeft(this); } set { Mapping.SetLeft(this, value); } }
        /// <summary>
        /// Defines the right coordinate of the control
        /// </summary>
        public double Right { get { return Mapping.GetRight(this); } set { Mapping.SetRight(this, value); } }
        /// <summary>
        /// Defines the bottom coordinate of the control
        /// </summary>
        public double Bottom { get { return Mapping.GetBottom(this); } set { Mapping.SetBottom(this, value); } }
        /// <summary>
        /// Get or set the width of the control
        /// </summary>
        public new double Width { get { return Mapping.GetWidth(this); } set { Mapping.SetWidth(this, value); } }
        /// <summary>
        /// Get or set the height of the control
        /// </summary>
        public new double Height { get { return Mapping.GetHeight(this); } set { Mapping.SetHeight(this, value); } }
        /// <summary>
        /// Gets the top-left coordinate of the control with respect to the global coordinate space
        /// </summary>
        /// <returns></returns>
        public Point GetGlobalTopLeft() { return new Point(0, 0); }

        // Child Object Management
        /// <summary>
        /// Indicates whether this control has any child controls
        /// </summary>
        /// <returns>True if this control has children; false otherwise</returns>
        public bool HasChildren()
        {
            return ((mapped.Children.Count + unmapped.Children.Count + drawingBoard.Children.Count) > 0);
        }
        /// <summary>
        /// Gets a list of this control's child controls 
        /// </summary>
        /// <returns>A list of IMappingControls that are children of this control</returns>
        public List<IMappingControl> GetChildren()
        {
            List<IMappingControl> childList = new List<IMappingControl>();
            childList.AddRange(Mapping.GetChildren(mapped));
            childList.AddRange(Mapping.GetChildren(unmapped));
            childList.AddRange(Mapping.GetChildren(drawingBoard));
            return childList;
        }
        /// <summary>
        /// Aligns all children within this control
        /// </summary>
        public void AlignChildren() { Mapping.AlignChildren(this, this.AlignmentColumns, this.AlignmentSpacing); }
        /// <summary>
        /// Removes all children from this control
        /// </summary>
        public void ClearChildren()
        {
            foreach (IMappingControl imc in GetChildren())
            {
                imc.ClearChildren();
            }
            mapped.Children.Clear();
            unmapped.Children.Clear();
            drawingBoard.Children.Clear();
            InitializeControlCollections();
        }
        /// <summary>
        /// Function used to determine if another mapping control can be placed within this one
        /// </summary>
        /// <param name="o">A control to be tested for compatibility</param>
        /// <returns>True if this control can contain the other; false otherwise</returns>
        public bool CanAccept(Object o) { return Mapping.CanXContainY(this, o); }
        /// <summary>
        /// Gets the child of the specified type and ID
        /// </summary>
        /// <param name="ID">The ID of the child to retrieve</param>
        /// <param name="ChildType">The type of the child to retrieve</param>
        /// <returns>The child if it was found; null otherwise</returns>
        public IMappingControl GetChild(string ID, Type ChildType)
        {
            foreach (Object o in Unmapped.Children)
            {
                if (o.GetType() == ChildType)
                {
                    if (((IMappingControl)o).MappingID == ID)
                        return (IMappingControl)o;
                }
            }
            foreach (Object o in Mapped.Children)
            {
                if (o is IMappingControl)
                {
                    Object result = ((IMappingControl)o).GetChild(ID, ChildType);
                    if (result != null)
                        return (IMappingControl)result;
                }
            }
            return null;
        }
        /// <summary>
        /// Function used to locate a child object at a mouse location
        /// </summary>
        /// <param name="mouse">The mouse device used to locate the mouse</param>
        /// <returns>A object, if one was found, at the location of the mouse</returns>
        public IMappingControl GetObjectAtMouse(MouseDevice mouse)
        {
            double top;
            double left;
            double right;
            double bottom;
            Point p = mouse.GetPosition(this);
            double x = p.X;
            double y = p.Y;
            IMappingControl locatedObject = null;
            foreach (Object o in root.Children)
            {
                if (o.GetType() == typeof(Canvas))
                {
                    // Skip the drawingBoard
                    if (o == drawingBoard)
                        continue;

                    Canvas c = (Canvas)o;
                    top = (double)c.GetValue(Canvas.TopProperty);
                    left = (double)c.GetValue(Canvas.LeftProperty);
                    right = (double)c.GetValue(Canvas.RightProperty);
                    bottom = (double)c.GetValue(Canvas.BottomProperty);

                    if (((x >= left) && (x <= right)) &&
                       ((y >= top) && (y <= bottom)))
                    {
                        foreach (Object child in c.Children)
                        {
                            if (Mapping.IsControl(o))
                            {
                                IMappingControl mapControl = (IMappingControl)child;
                                locatedObject = mapControl.GetObjectAtMouse(mouse);
                            }
                        }
                    }
                }
                if (locatedObject != null)
                {
                    break;
                }
            }
            return locatedObject;
        }

        // Parent Object Management 
        /// <summary>
        /// Gets the parent control to which this is currently attached
        /// </summary>
        public IMappingControl AttachedParent
        {
            get
            {
                return this;
            }
            set
            {
            }
        }
        /// <summary>
        /// Gets the parent control to which this was most recently attached
        /// </summary>
        public IMappingControl RecentParent
        {
            get
            {
                return this;
            }
            set
            {
            }
        }
        /// <summary>
        /// Attaches the control to the specified parent
        /// </summary>
        /// <param name="newParent"></param>
        public void AttachToParent(IMappingControl newParent) { }
        /// <summary>
        /// Detaches the control from its current parent
        /// </summary>
        public void DetachFromParent() { }
        /// <summary>
        /// Attaches the control to its most recent parent
        /// </summary>
        public void ReAttachToParent() { }

        // Visibility
        /// <summary>
        /// Shows the control
        /// </summary>
        public void Show()
        {
            this.Visibility = Visibility.Visible;
        }
        /// <summary>
        /// Hides the control
        /// </summary>
        public void Hide()
        {
            this.Visibility = Visibility.Hidden;
        }

        #endregion
        
        #region Mouse Events

        void MappingCanvasControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (UIMode == InterfaceMode.ManualMapping)
            {
                if (bDragging)
                {
                    Release(e.MouseDevice);
                }
            }
            else if (UIMode == InterfaceMode.GetInformation)
            {
                RaiseItemInformation(GetControlAtMouse(e.MouseDevice));
            }
        }
        void MappingCanvasControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (UIMode == InterfaceMode.ManualMapping)
            {
                if (!bDragging)
                {
                    IMappingControl imCtrl = GetControlAtMouse(e.MouseDevice);
                    if ((imCtrl != this) && (imCtrl != null))
                    {
                        Grab(imCtrl);
                    }
                }
            }
        }
        void MappingCanvasControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (UIMode == InterfaceMode.ManualMapping)
            {
                if (bDragging)
                {
                    if (oDragObject != null)
                    {
                        Point p = e.MouseDevice.GetPosition(this);
                        oDragObject.SetValue(Canvas.LeftProperty, p.X);
                        oDragObject.SetValue(Canvas.TopProperty, p.Y);
                        e.Handled = true;
                    }
                }
            }
            else if (UIMode == InterfaceMode.GetInformation)
            {
            }
        }
        #endregion

        #region User Interface Mode (Map / Info)
        /// <summary>
        /// Enumeration of Mapping UI Modes
        /// </summary>
        public enum InterfaceMode : int
        {
            /// <summary>
            /// Manual, drag-and-drop mapping mode
            /// </summary>
            ManualMapping = 1,
            /// <summary>
            /// Query of selected item mode
            /// </summary>
            GetInformation = 2
        }
        private InterfaceMode _UIMode;
        /// <summary>
        /// Get or set the mode the mapping UI is operating in
        /// </summary>
        public InterfaceMode UIMode
        {
            get
            {
                return _UIMode;
            }
            set
            {
                if (bDragging)
                {
                    RevertDrag();
                }
                _UIMode = value;
            }
        }
        #endregion

        #region Object Information (GetInformation Mode)

        /// <summary>
        /// Delegate that defines a function to receive information generated during query mode
        /// </summary>
        /// <param name="Type">The type of the object queried</param>
        /// <param name="ID">The ID of the object queried</param>
        /// <param name="Name">The name of the object queried</param>
        /// <param name="Information">Information about the object queried</param>
        public delegate void ItemInformationHandler(string Type, string ID, string Name, string Information);

        /// <summary>
        /// Event that is fired when an object is selected while operating in Query mode
        /// </summary>
        public event ItemInformationHandler ItemInformationRecevied;

        private void RaiseItemInformation(IMappingControl imap)
        {
            if (ItemInformationRecevied == null)
                return;
            if (imap.GetType() == typeof(MappingCanvasControl))
                return;

            string Type = string.Empty;
            string ID = imap.MappingID;
            string Name = imap.MappingName;
            string InformationString = string.Empty;

            if (imap.GetType() == typeof(MappingClusterControl))
            {
                Type = String.Format("FPGA Cluster / Board ({0})", ID);
                FalconMapping_Cluster cluster = (FalconMapping_Cluster)MapObjects.GetClusters()[ID];
                StringBuilder Info = new StringBuilder();
                Info.AppendLine("Member FPGAs");
                foreach (object o in cluster.GetMembers())
                {
                    Info.AppendLine(String.Format("    {0}", ((FalconMapping_FPGA)o).ID));
                }
                ItemInformationRecevied(Type, ID, Name, InformationString);
            }
            else if (imap.GetType() == typeof(MappingFPGAControl))
            {
                string Arch = MapObjects.GetFPGAArchitecture(ID);
                Type = String.Format("FPGA ({0}, {1})", Arch, ID);
                FalconMapping_FPGA fpga = (FalconMapping_FPGA)MapObjects.GetFPGAs()[ID];
                // Get Resources Available/Used
                StringBuilder Info = new StringBuilder();
                Info.AppendLine(String.Format("{0}: {1} / {2} / {3}", "Resource", "Total", "Used", "Available"));
                foreach (string res in fpga.GetTotalResources().Keys)
                {
                    long total = fpga.GetTotalResource(res);
                    long used = fpga.GetUsedResource(res);
                    long avail = fpga.GetAvailableResource(res);
                    Info.AppendLine(String.Format("    {0}: {1} / {2} / {3}", res, total, used, avail));
                }
                Info.AppendLine("--------------------");
                Info.AppendLine("Mapped Groups");
                foreach (object o in fpga.GetMappedGroups())
                {
                    Info.AppendLine(String.Format("    {0}", ((FalconMapping_Group)o).ID));
                }
                ItemInformationRecevied(Type, ID, Name, Info.ToString());
            }
            else if (imap.GetType() == typeof(MappingGroupControl))
            {
                Type = String.Format("Component Group ({0})", ID);
                FalconMapping_Group group = (FalconMapping_Group)MapObjects.GetGroups()[ID];
                // Get Resources Required / Member components
                StringBuilder Info = new StringBuilder();
                Info.AppendLine("Resources Required");
                foreach (string res in group.GetRequiredResources().Keys)
                {
                    long resNeeded = group.GetRequiredResource(res);
                    Info.AppendLine(String.Format("    {0}: {1}", res, resNeeded));
                }
                Info.AppendLine("--------------------");
                Info.AppendLine("Component Members");
                foreach (object o in group.GetGroupedComponents())
                {
                    Info.AppendLine(String.Format("    {0}", ((FalconMapping_Component)o).ID));
                }
                ItemInformationRecevied(Type, ID, Name, Info.ToString());
            }
            else if (imap.GetType() == typeof(MappingComponentControl))
            {
                Type = String.Format("Component ({0})", ID);
                FalconMapping_Component component = (FalconMapping_Component)MapObjects.GetComponents()[ID];
                // Get Resources Required
                StringBuilder Info = new StringBuilder();
                Info.AppendLine("Resources Required");
                foreach (string res in component.GetRequiredResources().Keys)
                {
                    long resNeeded = component.GetRequiredResource(res);
                    Info.AppendLine(String.Format("    {0}: {1}", res, resNeeded));
                }
                ItemInformationRecevied(Type, ID, Name, Info.ToString());
            }
            else if (imap.GetType() == typeof(MappingConnectionControl))
            {
                Type = String.Format("Component Connection ({0})", ID);
                // Get Data Weight / Source-Sink Components
                ItemInformationRecevied(Type, ID, Name, InformationString);
            }
            else if (imap.GetType() == typeof(MappingLinkControl))
            {
                Type = String.Format("FPGA Link ({0})", ID);
                // Get Link Speed Utilization / Source-Sink FPGAs
                ItemInformationRecevied(Type, ID, Name, InformationString);
            }
        }

        #endregion 

        #region Drag 'n Drop (ManualMapping Mode)
        private bool bDragging = false;
        private IMappingControl oDragObject = null;

        /// <summary>
        /// Get or set whether events that indicate changes in Mapping State are suspended. (Typically during automatic mapping, to prevent multiple concurrent events from firing)
        /// </summary>
        public bool SuspendMappingStateEvents { get; set; }
        /// <summary>
        /// Returns the WPF canvas located beneath the mouse
        /// </summary>
        /// <param name="mouse">The mouse device used for locating the canvas</param>
        /// <returns>A WPF canvas object located beneath the mouse, if one was located; null otherwise</returns>
        public Canvas GetCanvasAtMouse(MouseDevice mouse)
        {
            double top;
            double left;
            double right;
            double bottom;
            Point p = mouse.GetPosition(this);
            double x = p.X;
            double y = p.Y;

            Canvas locatedCanvas = root;
            foreach (Object o in root.Children)
            {
                if (o.GetType() == typeof(Canvas))
                {
                    // Skip the drawingBoard
                    if (o == drawingBoard)
                        continue;

                    Canvas c = (Canvas)o;
                    top = (double)c.GetValue(Canvas.TopProperty);
                    left = (double)c.GetValue(Canvas.LeftProperty);
                    right = (double)c.GetValue(Canvas.RightProperty);
                    bottom = (double)c.GetValue(Canvas.BottomProperty);

                    if (((x >= left) && (x <= right)) &&
                       ((y >= top) && (y <= bottom)))
                    {
                        locatedCanvas = c;
                    }
                }
                if (locatedCanvas != null)
                {
                    break;
                }
            }
            return locatedCanvas;
        }
        /// <summary>
        /// Returns the IMappingControl object located beneath the mouse
        /// </summary>
        /// <param name="mouse">The mouse device used for locating the canvas</param>
        /// <returns>An IMappingControl object located beneath the mouse, if one was located; null otherwise</returns>
        public IMappingControl GetControlAtMouse(MouseDevice mouse)
        {
            double top;
            double left;
            double right;
            double bottom;
            Point p = mouse.GetPosition(this);
            double x = p.X;
            double y = p.Y;

            IMappingControl locatedObject = this;
            foreach (Object o in root.Children)
            {
                if (o.GetType() == typeof(Canvas))
                {
                    // Skip the drawingBoard
                    if (o == drawingBoard)
                        continue;

                    Canvas c = (Canvas)o;
                    top = (double)c.GetValue(Canvas.TopProperty);
                    left = (double)c.GetValue(Canvas.LeftProperty);
                    right = (double)c.GetValue(Canvas.RightProperty);
                    bottom = (double)c.GetValue(Canvas.BottomProperty);

                    if (((x >= left) && (x <= right)) &&
                       ((y >= top) && (y <= bottom)))
                    {
                        foreach (Object ctrl in c.Children)
                        {
                            if (ctrl is IMappingControl)
                            {
                                IMappingControl imap = ((IMappingControl)ctrl).GetObjectAtMouse(mouse);
                                if (imap != null)
                                {
                                    locatedObject = imap;
                                    break;
                                }
                            }
                        }
                    }
                }
                if (locatedObject != this)
                {
                    break;
                }
            }
            return locatedObject;
        }
        /// <summary>
        /// Begins drag-and-drop by 'grabbing' the specified IMappingControl
        /// </summary>
        /// <param name="o">The IMappingControl to begin drag-and-drop with</param>
        public void Grab(IMappingControl o)
        {
            if (bDragging)
                return;
            if (oDragObject != null)
                return;

            if (o is MappingClusterControl)
                return;
            if (o is MappingFPGAControl)
                return;

            try
            {
                if (Mapping.IsControl(o))
                {
                    Point p = Mouse.GetPosition(drawingBoard);
                    oDragObject = o;
                    oDragObject.DetachFromParent();
                    HideInterconnects();

                    double minAspect = (oDragObject.Width < oDragObject.Height ? oDragObject.Width : oDragObject.Height);
                    oDragObject.Top = p.Y;
                    oDragObject.Left = p.Y;
                    oDragObject.Width = minAspect / 2;
                    oDragObject.Height = minAspect / 2;

                    drawingBoard.Children.Add((UIElement)oDragObject);
                    bDragging = true;
                }
            }
            catch (Exception ex)
            {
                ErrorReporting.TraceException(ex);
                RevertDrag();
            }
            //if (oDragObject != null)
            //    oDragObject.AlignChildren();
            //this.RefreshDisplay();
        }
        /// <summary>
        /// Ends drag-and-drop by 'releasing' the previously grabbed IMappingControl at the mouse location
        /// </summary>
        /// <param name="mouse">The location of the mouse when the drag-and-drop was released</param>
        public void Release(MouseDevice mouse)
        {
            if (!bDragging)
                return;
            if (oDragObject == null)
                return;
            
            try
            {
                drawingBoard.Children.Remove((UIElement)oDragObject);
                Point p = mouse.GetPosition(this);
                IMappingControl possibleContainer = GetControlAtMouse(mouse);
                if (possibleContainer != null)
                {
                    IMappingControl container = Mapping.GetEligibleContainer(oDragObject, possibleContainer);
                    oDragObject.AttachToParent(container);
                }
                else
                {
                    RevertDrag();
                }

                double minAspect = (oDragObject.Width < oDragObject.Height ? oDragObject.Width : oDragObject.Height);
                oDragObject.Top = p.Y;
                oDragObject.Left = p.Y;
                oDragObject.Width = minAspect * 2;
                oDragObject.Height = minAspect * 2;
                oDragObject = null;
                bDragging = false;
                this.RefreshDisplay();
            }
            catch (Exception ex)
            {
                ErrorReporting.TraceException(ex);
                RevertDrag();
            }
        }
        private void RevertDrag()
        {
            if (oDragObject == null)
                return;
            ((IMappingControl)oDragObject).ReAttachToParent();
            oDragObject = null;
            bDragging = false;
            this.RefreshDisplay();
        }
        #endregion

        #region Mapping Control Collections/Monitoring - Mapping Algorithm Library Interface

        Dictionary<string, MappingComponentControl> ComponentControls;
        Dictionary<string, MappingFPGAControl> FPGAControls;
        Dictionary<string, MappingGroupControl> GroupControls;
        Dictionary<string, MappingClusterControl> ClusterControls;
        Dictionary<string, MappingConnectionControl> ConnectionControls;
        Dictionary<string, MappingLinkControl> LinkControls;

        /// <summary>
        /// Initializes empty mapping control collections
        /// </summary>
        public void InitializeControlCollections()
        {
            ComponentControls = new Dictionary<string, MappingComponentControl>();
            FPGAControls = new Dictionary<string, MappingFPGAControl>();
            GroupControls = new Dictionary<string, MappingGroupControl>();
            ClusterControls = new Dictionary<string, MappingClusterControl>();
            ConnectionControls = new Dictionary<string, MappingConnectionControl>();
            LinkControls = new Dictionary<string, MappingLinkControl>();
        }

        /// <summary>
        /// Draws all links on the drawing canvas
        /// </summary>
        public void DrawLinks()
        {
            foreach (MappingLinkControl link in LinkControls.Values)
            {
                link.Draw(drawingBoard);
            }
        }
        /// <summary>
        /// Draws all connections on the drawing canvas
        /// </summary>
        public void DrawConnections()
        {
            foreach (MappingConnectionControl conn in ConnectionControls.Values)
            {
                conn.Draw(drawingBoard);
            }
        }

        /// <summary>
        /// MappingAlgorithm library management object
        /// </summary>
        public FalconMapping_Objects MapObjects;
        /// <summary>
        /// Resets all mapping controls and state
        /// </summary>
        public void ResetMappingDisplay()
        {
            MapObjects = new FalconMapping_Objects();
            MapObjects.Reset();
            MapObjects.ExProcessor += new FalconMapping_Objects.ExceptionProcessor(RaiseMappingError);
            this.ClearChildren();
        }

        /// <summary>
        /// Loads mapping information from the project files specified in the passed project manager
        /// </summary>
        /// <param name="ProjMan">The Project Manager object managing the project being mapped</param>
        /// <param name="ResetMapping">Flag indicating whether a previously saved mapping state should be loaded, if it exists.  If this argument is True,
        /// any previous saved state will be ignored and, if it exists, deleted.</param>
        public void LoadProject(CerebrumProjectManager.ProjectManager ProjMan, bool ResetMapping)
        {
            try
            {
                _ProjMan = ProjMan;
                ResetMappingDisplay();
                InitializeControlCollections();

                MapObjects.PathMan = _ProjMan.PathManager;
                MapObjects.ReadProjectFiles(ProjMan.DesignFile.FullName,
                                        ProjMan.CommunicationsFile.FullName);
                if (ProjMan.ProjectLoaded)
                {
                    FileInfo mappingStateFile = new FileInfo(String.Format("{0}\\{1}", _ProjMan.ProjectDirectory, DEFAULT_MAPPING_STATE_FILE));
                    if (mappingStateFile.Exists)
                    {
                        if (ResetMapping)
                        {
                            mappingStateFile.Delete();
                        }
                    }
                }
                LoadState();
                //PopulateFromMapObjects();
            }
            catch (Exception ex)
            {
                ErrorReporting.TraceException(ex);
            }
        }
        /// <summary>
        /// Saves the current state of the mapping to a file.
        /// </summary>
        /// <param name="TargetFile">The file to which the current mapping state is to be saved.</param>
        public void SaveState(string TargetFile)
        {
            if ((_ProjMan != null) && (MapObjects != null))
            {
                if (_ProjMan.ProjectLoaded)
                {
                    MapObjects.SaveIntermediateState(TargetFile);
                }
            }
        }
        /// <summary>
        /// Saves the current state of the mapping to the default file.
        /// </summary>
        public void SaveState()
        {
            if ((_ProjMan != null) && (MapObjects != null))
            {
                if (_ProjMan.ProjectLoaded)
                {
                    FileInfo mappingStateFile = new FileInfo(String.Format("{0}\\{1}", _ProjMan.ProjectDirectory, DEFAULT_MAPPING_STATE_FILE));
                    MapObjects.SaveIntermediateState(mappingStateFile.FullName);
                }
            }
        }
        /// <summary>
        /// Loads the current state of the mapping from a file.
        /// </summary>
        /// <param name="SourceFile">The file from which the current mapping state is to be loaded.</param>
        public void LoadState(string SourceFile)
        {
            if ((_ProjMan != null) && (MapObjects != null))
            {
                if (_ProjMan.ProjectLoaded)
                {
                    MapObjects.LoadIntermediateState(SourceFile);
                    PopulateFromMapObjects();
                }
            }
        }
        /// <summary>
        /// Loads the current state of the mapping from a file.
        /// </summary>
        public void LoadState()
        {
            if ((_ProjMan != null) && (MapObjects != null))
            {
                if (_ProjMan.ProjectLoaded)
                {
                    FileInfo mappingStateFile = new FileInfo(String.Format("{0}\\{1}", _ProjMan.ProjectDirectory, DEFAULT_MAPPING_STATE_FILE));
                    if (mappingStateFile.Exists)
                    {
                        MapObjects.LoadIntermediateState(mappingStateFile.FullName);
                    }
                    PopulateFromMapObjects();
                }
            }
        }
        
        /// <summary>
        /// Populate mapping GUI objects based on current state of MappingAlgorithm library
        /// </summary>
        public void PopulateFromMapObjects()
        {
            try
            {
                Mapping.Populating = true;

                #region Setup the Mapping Canvas
                RecalibrateCanvasSize();
                this.AlignmentColumns = ColumnsPerCanvas;
                this.AlignmentSpacing = SpacingPerGroup;
                this.ClearChildren();

                #endregion

                #region Clusters
                Dictionary<string, FalconMapping_Cluster> Clusters = MapObjects.GetClusters();
                foreach (string k in Clusters.Keys)
                {
                    FalconMapping_Cluster c = Clusters[k];

                    MappingClusterControl ctrl = CreateClusterControl(c.ID, c.Name, this);
                    ((IMappingControl)ctrl).Show();
                }
                #endregion

                #region FPGAs
                Dictionary<string, FalconMapping_FPGA> FPGAs = MapObjects.GetFPGAs();
                foreach (string k in FPGAs.Keys)
                {
                    FalconMapping_FPGA c = FPGAs[k];
                    IMappingControl parent = this;
                    if (c.IsClustered)
                    {
                        if (ClusterControls.Keys.Contains(c.ClusterID))
                            parent = ClusterControls[c.ClusterID];
                    }
                    MappingFPGAControl ctrl = CreateFPGAControl(c.ID, c.Name, parent);
                    ((IMappingControl)ctrl).Show();
                }
                #endregion

                #region Groups
                Dictionary<string, FalconMapping_Group> Groups = MapObjects.GetGroups();
                foreach (string k in Groups.Keys)
                {
                    FalconMapping_Group c = Groups[k];
                    IMappingControl parent = this;
                    if (c.IsMapped)
                    {
                        if (FPGAControls.Keys.Contains(c.TargetFPGA))
                            parent = FPGAControls[c.TargetFPGA];
                    }

                    MappingGroupControl ctrl = CreateGroupControl(c.ID, c.Name, parent);
                    ((IMappingControl)ctrl).Show();
                }
                #endregion

                #region Components
                Dictionary<string, FalconMapping_Component> Components = MapObjects.GetComponents();
                foreach (string k in Components.Keys)
                {
                    FalconMapping_Component c = Components[k];
                    IMappingControl parent = this;
                    if (c.IsGrouped)
                    {
                        if (GroupControls.Keys.Contains(c.GroupID))
                            parent = GroupControls[c.GroupID];
                    }

                    MappingComponentControl ctrl = CreateComponentControl(c.ID, c.Name, parent);
                    ctrl.Show();
                }
                #endregion

                #region Connections
                Dictionary<string, FalconMapping_Connection> Connections = MapObjects.GetConnections();
                foreach (string k in Connections.Keys)
                {
                    FalconMapping_Connection c = Connections[k];
                    CreateConnectionControl(c.ID, c.SourceComponent, c.SinkComponent, this);
                }
                #endregion

                #region Links
                Dictionary<string, FalconMapping_Link> Links = MapObjects.GetLinks();
                foreach (string k in Links.Keys)
                {
                    FalconMapping_Link c = Links[k];
                    CreateLinkControl(c.ID, c.SourceFPGA, c.SinkFPGA, this);
                }
                #endregion

                this.RefreshDisplay();
                StateChanged();
            }
            catch (Exception ex)
            {
                ErrorReporting.DebugException(ex);
            }
            finally
            {
                Mapping.Populating = false;
            }
        }
        /// <summary>
        /// Executes auto-mapping of remaining unmapped components
        /// </summary>
        /// <returns>True if auto-mapping was completed successfully</returns>
        public bool CompleteMapping()
        {
            bool bSuccess = false;
            try
            {
                bSuccess = MapObjects.DoMapping();
            }
            catch (Exception ex)
            {
                RaiseMappingError(ex, ex.Message);
            }
            PopulateFromMapObjects();
            return bSuccess;
        }
        /// <summary>
        /// Saves the current mapping state, XPS mapping, and routing
        /// </summary>
        public void SaveMapping()
        {
            if (CompleteMapping())
            {
                MapObjects.WriteFinalOutputs();
                MapObjects.WriteComponentAddressMap(_ProjMan.PathManager["AddressMap"]);
                MapObjects.WriteSystemFile(_ProjMan.PathManager["LocalProjectRoot"] + "\\system_spec.xml");
                MapObjects.WriteMappingFile(_ProjMan.PathManager["LocalProjectRoot"] + "\\system_map.xml", false, true);
                MapObjects.GenerateRouting(_ProjMan.PathManager["LocalProjectRoot"] + "\\routing.xml");
                MapObjects.UpdateInstanceMappings(_ProjMan.DesignFile.FullName, _ProjMan.CommunicationsFile.FullName);
                //MapObjects.SaveFlows();
                MessageBox.Show("Component Mapping Saved!", "Confirm", MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        /// <summary>
        /// Queries the mapping library to determine whether all components that have been loaded, have been mapped.
        /// </summary>
        /// <returns>True if all loaded components have been mapped, False otherwise.</returns>
        public bool AllComponentsMapped()
        {
            if (MapObjects != null)
            {
                return MapObjects.AllComponentsMapped();
            }
            return false;
        }

        internal MappingClusterControl CreateClusterControl(string ID, string Name, IMappingControl parent)
        {
            if (ClusterControls.Keys.Contains(ID))
                return ClusterControls[ID];

            MappingClusterControl ctrl = new MappingClusterControl();
            ((IMappingControl)ctrl).MappingID = ID;
            ((IMappingControl)ctrl).MappingName = Name;
            ((IMappingControl)ctrl).AlignmentColumns = ColumnsPerCluster;
            ((IMappingControl)ctrl).AlignmentSpacing = SpacingPerCluster;
            ((IMappingControl)ctrl).Top = 10;
            ((IMappingControl)ctrl).Left = 10;
            ((IMappingControl)ctrl).Height = 10;
            ((IMappingControl)ctrl).Width = 10;
            ((IMappingControl)ctrl).BackgroundBrush = CLUSTER_BRUSH;
            ((IMappingControl)ctrl).ParentCanvas = this;
            ((IMappingControl)ctrl).AttachToParent(this);

            ((ICollapsible)ctrl).InitCollapsible(0.5, 0.5);

            ctrl.BorderThickness = new Thickness(1.0);
            ctrl.BorderBrush = Brushes.Black;
            ClusterControls.Add(ctrl.MappingID, ctrl);
            return ctrl;
        }
        internal MappingFPGAControl CreateFPGAControl(string ID, string Name, IMappingControl parent)
        {
            if (FPGAControls.Keys.Contains(ID))
                return FPGAControls[ID];

            MappingFPGAControl ctrl = new MappingFPGAControl();
            ((IMappingControl)ctrl).MappingID = ID;
            ((IMappingControl)ctrl).MappingName = Name;
            ((IMappingControl)ctrl).AlignmentColumns = ColumnsPerFPGA;
            ((IMappingControl)ctrl).AlignmentSpacing = SpacingPerFPGA;
            ((IMappingControl)ctrl).Top = 10;
            ((IMappingControl)ctrl).Left = 10;
            ((IMappingControl)ctrl).Height = 10;
            ((IMappingControl)ctrl).Width = 10;
            ((IMappingControl)ctrl).BackgroundBrush = FPGA_BRUSH;
            ((IMappingControl)ctrl).ParentCanvas = this;
            ((IMappingControl)ctrl).AttachToParent(parent);

            ((ICollapsible)ctrl).InitCollapsible(0.5, 0.5);

            ctrl.BorderThickness = new Thickness(1.0);
            ctrl.BorderBrush = Brushes.Black;
            FPGAControls.Add(ctrl.MappingID, ctrl);
            return ctrl;
        }
        internal MappingGroupControl CreateGroupControl(string ID, string Name, IMappingControl parent)
        {
            if (GroupControls.Keys.Contains(ID))
                return GroupControls[ID];

            if (!MapObjects.GetGroups().ContainsKey(ID))
                MapObjects.AddGroup(ID, Name);

            MappingGroupControl ctrl = new MappingGroupControl();
            ((IMappingControl)ctrl).MappingID = ID;
            ((IMappingControl)ctrl).MappingName = Name;
            ((IMappingControl)ctrl).AlignmentColumns = ColumnsPerGroup;
            ((IMappingControl)ctrl).AlignmentSpacing = SpacingPerGroup;
            ((IMappingControl)ctrl).Top = 10;
            ((IMappingControl)ctrl).Left = 10;
            ((IMappingControl)ctrl).Height = 10;
            ((IMappingControl)ctrl).Width = 10;
            ((IMappingControl)ctrl).BackgroundBrush = GROUP_BRUSH;
            ((IMappingControl)ctrl).ParentCanvas = this;
            ((IMappingControl)ctrl).AttachToParent(parent);

            ((ICollapsible)ctrl).InitCollapsible(0.5, 0.5);

            ctrl.BorderThickness = new Thickness(1.0);
            ctrl.BorderBrush = Brushes.Black;
            GroupControls.Add(ctrl.MappingID, ctrl);
            return ctrl;
        }
        internal MappingComponentControl CreateComponentControl(string ID, string Name, IMappingControl parent)
        {
            if (ComponentControls.Keys.Contains(ID))
                return ComponentControls[ID];

            MappingComponentControl ctrl = new MappingComponentControl();
            ((IMappingControl)ctrl).MappingID = ID;
            ((IMappingControl)ctrl).MappingName = Name;
            ((IMappingControl)ctrl).Top = 10;
            ((IMappingControl)ctrl).Left = 10;
            ((IMappingControl)ctrl).Height = 10;
            ((IMappingControl)ctrl).Width = 10;
            ((IMappingControl)ctrl).BackgroundBrush = Brushes.Transparent;
            ctrl.ImageFile = _ProjMan.GetCoreInstanceImage(ID);

            ((IMappingControl)ctrl).ParentCanvas = this;
            ((IMappingControl)ctrl).AttachToParent(parent);

            ctrl.BorderThickness = new Thickness(1.0);
            ctrl.BorderBrush = Brushes.Black;
            ComponentControls.Add(ctrl.MappingID, ctrl);
            return ctrl;
        }
        internal MappingConnectionControl CreateConnectionControl(string ID, string SourceID, string SinkID, IMappingControl parent)
        {
            if (ConnectionControls.Keys.Contains(ID))
                return ConnectionControls[ID];

            MappingConnectionControl ctrl = new MappingConnectionControl();

            ((IMappingControl)ctrl).MappingID = ID;
            ((IMappingControl)ctrl).MappingName = Name;

            MappingComponentControl source = null;
            MappingComponentControl sink = null;
            foreach (MappingComponentControl mc in ComponentControls.Values)
            {
                if (mc.MappingID == SourceID)
                    source = mc;
                if (mc.MappingID == SinkID)
                    sink = mc;

                if ((source != null) && (sink != null))
                    break;
            }
            if ((source != null) && (sink != null))
            {
                source.Outputs.Add(ctrl);
                sink.Inputs.Add(ctrl);
                ctrl.BackgroundBrush = CONNECTION_BRUSH;
                ctrl.SetSource(source, new Point(0.5, 0.5));
                ctrl.SetSink(sink, new Point(0.5, 0.5));
                ((IMappingControl)ctrl).ParentCanvas = this;
                ((IMappingControl)ctrl).AttachToParent(this);
                ConnectionControls.Add(ctrl.MappingID, ctrl);
                return ctrl;
            }
            else
            {
                return null;
            }
        }
        internal MappingLinkControl CreateLinkControl(string ID, string SourceID, string SinkID, IMappingControl parent)
        {
            if (LinkControls.Keys.Contains(ID))
                return LinkControls[ID];

            MappingLinkControl ctrl = new MappingLinkControl();
            ((IMappingControl)ctrl).MappingID = ID;
            ((IMappingControl)ctrl).MappingName = Name;

            MappingFPGAControl source = null;
            MappingFPGAControl sink = null;
            foreach (MappingFPGAControl mc in FPGAControls.Values)
            {
                if (mc.MappingID == SourceID)
                    source = mc;
                if (mc.MappingID == SinkID)
                    sink = mc;

                if ((source != null) && (sink != null))
                    break;
            }
            if ((source != null) && (sink != null))
            {
                source.Outputs.Add(ctrl);
                sink.Inputs.Add(ctrl);
                ctrl.BackgroundBrush = LINK_BRUSH;
                ctrl.SetSource(source, new Point(1.0, 0.5));
                ctrl.SetSink(sink, new Point(0.0, 0.5));
                ((IMappingControl)ctrl).ParentCanvas = this;
                ((IMappingControl)ctrl).AttachToParent(this);
                LinkControls.Add(ctrl.MappingID, ctrl);
                return ctrl;
            }
            else
            {
                return null;
            }
        }

        private void RedrawInterconnects()
        {
            foreach (MappingConnectionControl ctrl in ConnectionControls.Values)
                ctrl.Show();
            foreach (MappingLinkControl ctrl in LinkControls.Values)
                ctrl.Show();
        }
        private void HideInterconnects()
        {
            foreach (MappingConnectionControl ctrl in ConnectionControls.Values)
                ctrl.Hide();
            foreach (MappingLinkControl ctrl in LinkControls.Values)
                ctrl.Hide();
        }
        #endregion

        #region Mapping GUI ListView Interface
        /// <summary>
        /// Enforces manual mapping of a component to an FPGA
        /// </summary>
        /// <param name="ComponentID">The ID of the component to map</param>
        /// <param name="FPGA_ID">The ID of the FPGA to which the component is to be mapped</param>
        public void MapComponentToFPGA(string ComponentID, string FPGA_ID)
        {
            if ((ComponentControls.Keys.Contains(ComponentID)) &&
                    (FPGAControls.Keys.Contains(FPGA_ID)))
            {
                MappingComponentControl a = ComponentControls[ComponentID];
                MappingFPGAControl b = FPGAControls[FPGA_ID];
                a.AttachToParent(b);
            }
            this.RefreshDisplay();
        }
        /// <summary>
        /// Enforces a manual mapping of a component-group to an FPGA
        /// </summary>
        /// <param name="GroupID">The ID of the group to map</param>
        /// <param name="FPGA_ID">The ID of the FPGA to which the component is to be mapped</param>
        public void MapGroupToFPGA(string GroupID, string FPGA_ID)
        {
            if ((GroupControls.Keys.Contains(GroupID)) &&
                    (FPGAControls.Keys.Contains(FPGA_ID)))
            {
                MappingGroupControl a = GroupControls[GroupID];
                MappingFPGAControl b = FPGAControls[FPGA_ID];
                a.AttachToParent(b);
            }
            this.RefreshDisplay();
        }
        /// <summary>
        /// Adds the specified component to a group
        /// </summary>
        /// <param name="ComponentID">The ID of the component to group</param>
        /// <param name="GroupID">The ID of the group to map</param>
        public void AddComponentToGroup(string ComponentID, string GroupID)
        {
            if ((GroupControls.Keys.Contains(GroupID)) &&
                    (ComponentControls.Keys.Contains(ComponentID)))
            {
                MappingComponentControl a = ComponentControls[ComponentID];
                MappingGroupControl b = GroupControls[GroupID];
                a.AttachToParent(b);
            }
            this.RefreshDisplay();
        }
        /// <summary>
        /// Removes the specified component from any group it may be in
        /// </summary>
        /// <param name="ComponentID">The ID of the component to ungroup</param>
        public void UnGroupComponent(string ComponentID)
        {
            if ((ComponentControls.Keys.Contains(ComponentID)))
            {
                MappingComponentControl a = ComponentControls[ComponentID];
                a.AttachToParent(this);
            }
            this.RefreshDisplay();
        }
        /// <summary>
        /// Unmaps the specified component from any FPGA it may be mapped to
        /// </summary>
        /// <param name="GroupID">The ID of the group to unmap</param>
        public void UnMapGroup(string GroupID)
        {
            if ((GroupControls.Keys.Contains(GroupID)))
            {
                MappingGroupControl a = GroupControls[GroupID];
                a.AttachToParent(this);
            }
            this.RefreshDisplay();
        }
        /// <summary>
        /// Removes the component from any mapped groups it may be a part of
        /// </summary>
        /// <param name="ComponentID">The ID of the component to unmap</param>
        public void UnMapComponent(string ComponentID)
        {
            if ((ComponentControls.Keys.Contains(ComponentID)))
            {
                MappingComponentControl a = ComponentControls[ComponentID];
                a.AttachToParent(this);
            }
            this.RefreshDisplay();
        }
        /// <summary>
        /// Creates a new group with the specified ID and name
        /// </summary>
        /// <param name="GroupID">The desired ID of the new group</param>
        /// <param name="GroupName">The desired name of the new group</param>
        public void CreateGroup(string GroupID, string GroupName)
        {
            MappingGroupControl newGroup = CreateGroupControl(GroupID, GroupName, this);
            newGroup.Show();
            this.RefreshDisplay();
        }

        #endregion

        #region Mapping Events

        /// <summary>
        /// Delegate that defines a function used to notify the control's owner that the mapping system state has been changed
        /// </summary>
        public delegate void MappingStateChangedHandler();
        /// <summary>
        /// Event that is fired when the mapping system's state has changed
        /// </summary>
        public event MappingStateChangedHandler MappingStateChanged;
        internal void StateChanged()
        {
            if (!this.SuspendMappingStateEvents)
            {
                MappingStateChangedHandler eh = MappingStateChanged;
                if (eh != null)
                    eh();
            }
        }

        /// <summary>
        /// Delegate that defines a function used to notify the control's owner that an error has occurred in the mapping system
        /// </summary>
        /// <param name="Message">The message describing the error</param>
        public delegate void MappingErrorHandler(string Message);
        /// <summary>
        /// Event that is fired when a mapping error occurs
        /// </summary>
        public event MappingErrorHandler MappingError;
        /// <summary>
        /// Delegate that defines a function used to notify the control's owner that an exception has occurred in the mapping system
        /// </summary>
        /// <param name="ExceptionTrace">A trace of the exception</param>
        public delegate void MappingExceptionHandler(string ExceptionTrace);
        /// <summary>
        /// Event that is fired when an exception occurs in the mapping system
        /// </summary>
        public event MappingExceptionHandler MappingException;

        internal void RaiseMappingError(Exception ex, string Message)
        {
            if (ex is FalconMappingAlgorithm.Exceptions.MappingException)
            {
                MappingErrorHandler eh = MappingError;
                if (eh != null)
                {
                    eh(String.Format("{0}", ex.Message));
                }
            }
            else
            {
                MappingExceptionHandler eh = MappingException;
                if (eh != null)
                {
                    eh(ErrorReporting.ExceptionDetails(ex));
                }
            }

        }

        #endregion

        MessageEventController myMessages;
        /// <summary>
        /// Attach the specified MessageEventController to this control, allowing it to propagate messages to the GUI
        /// </summary>
        /// <param name="EventController"></param>
        public void AttachMessageController(MessageEventController EventController)
        {
            myMessages = EventController;
            Mapping.MappingMessenger = EventController;
        }
    }
}
